from gamelib import astar

def find_empty_adjacent_cell(map, start, target):
    '''Find an empty adjacent cell next to "target" which is closest to "start".
    '''
    ni, nj = start
    ci, cj = target
    if ni >= ci: ir = (1, 0, -1)
    else: ir = (-1, 0, 1)
    if nj >= cj: jr = (1, 0, -1)
    else: jr = (-1, 0, 1)
    for i in ir:
        for j in jr:
            ti = ci + i
            tj = cj + j
            if (ti, tj) == target: continue
            cell = map.get_cell(ti, tj)
            if cell.get('impassable'):
                continue
            return cell.position
    return None


def find_path(map, start, end, ignore=None, close_enough=False):
    '''Find a good path from start to end on the map.

    map is a cocos.tiles.RectMap
    start and end are (i, j) cell location tuples
    ignore is a list of objects to ignore
    close_enough indicates it's OK to get as close as possible to the end
    '''
    # the astar code uses tuples for identifying nodes, so make sure the locations
    # are tuples
    assert isinstance(start, tuple)
    assert isinstance(end, tuple)
    return astar.AStar(LevelHandler(map, ignore)).findPath(start, end, close_enough)

class LevelHandler:
    '''Solve paths in our game's Level.

    Sometimes we need to ignore the contents of a cell so we can make a
    path to it.
    '''

    def __init__(self, tilemap, ignore=None):
        self.m = tilemap
        self.ignore = ignore
        #print 'ignoring', self.ignore

    def getNode(self, location):
        ' location is just a tuple '
        return astar.Node(location, 1, location)

    def _handleNode(self, fromnode, cell, dest):
        x, y = cell.i, cell.j
        n = astar.Node((x, y), 1, (x, y))
        destx, desty = dest

        dx = abs(x - destx)
        dy = abs(y - desty)

        emCost = max(dx, dy)
        if dx and dy:
            # prefer going in straight lines
            emCost += 0.001
        n.mCost += fromnode.mCost
        n.score = n.mCost + emCost
        n.parent = fromnode
        return n

    def getAdjacentNodes(self, curnode, dest):
        map = self.m

        cell = map.get_cell(curnode.location[0], curnode.location[1],
            ignore=self.ignore)
        dl = map.get_cell(dest[0], dest[1], ignore=self.ignore)

        result = []
        #print 'getAdjacentNodes', cell

        # left
        n = map.get_neighbor(cell, map.LEFT, ignore=self.ignore)
        if n:
            if not n.get('impassable'):
                result.append(self._handleNode(curnode, n, dest))

            # diagonal up left
            m = map.get_neighbor(n, map.UP)
            if m and not m.get('impassable'):
                result.append(self._handleNode(curnode, m, dest))

            # diagonal down left
            m = map.get_neighbor(n, map.DOWN)
            if m and not m.get('impassable'):
                result.append(self._handleNode(curnode, m, dest))

        # right
        n = map.get_neighbor(cell, map.RIGHT, ignore=self.ignore)
        if n:
            if not n.get('impassable'):
                result.append(self._handleNode(curnode, n, dest))

            # diagonal up right
            m = map.get_neighbor(n, map.UP)
            if m and not m.get('impassable'):
                result.append(self._handleNode(curnode, m, dest))
            # diagonal down right
            m = map.get_neighbor(n, map.DOWN)
            if m and not m.get('impassable'):
                result.append(self._handleNode(curnode, m, dest))

        # down
        n = map.get_neighbor(cell, map.DOWN, ignore=self.ignore)
        if n and not n.get('impassable'):
            result.append(self._handleNode(curnode, n, dest))

        # up
        n = map.get_neighbor(cell, map.UP, ignore=self.ignore)
        if n and not n.get('impassable'):
            result.append(self._handleNode(curnode, n, dest))

        return result

